package deprecated;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ssm.basis.service.dictionary.DictEnum;
import com.ssm.basis.web.jackson.dictionary.BeanPropertyTranslate;
import com.ssm.basis.web.jackson.dictionary.Dict;
import com.ssm.basis.web.jackson.dictionary.IDictionaryTranslate;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.ssm.basis.service.util.ReflectionUtil.getAllField;
import static com.ssm.basis.service.util.TypeConversionHelper.castList;
import static java.util.Arrays.asList;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

/**
 * 翻译属性
 *
 * @see BeanPropertyTranslate
 */
@Deprecated
@RequiredArgsConstructor
// @ControllerAdvice(ROOT_PACKAGE)
class RestResultTranslate implements ResponseBodyAdvice<Object> {
    final ObjectMapper objectMapper;
    @Autowired(required = false)
    IDictionaryTranslate dictionaryTranslate;

    /**
     * 序列化时 is 开头方法名对应的属性没有 is
     */
    public static final String IS = "is";

    /**
     * 字典后缀
     */
    public static final String DICT_LABEL_SUFFIX = "_text";

    @Override
    public boolean supports(MethodParameter returnType, @Nullable Class<? extends HttpMessageConverter<?>> converterType) {
        // returnType.getMethod().getDeclaringClass().getName().startsWith("com.ssm")
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class) ||
                MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
    }

    @Override
    public Object beforeBodyWrite(Object body, @Nullable MethodParameter returnType, @Nullable MediaType selectedContentType,
                                  @Nullable Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  @Nullable ServerHttpRequest request, @Nullable ServerHttpResponse response) {
        // 转换成 JSON 树(需要存在 get 方法)，方便添加属性，https://blog.csdn.net/f641385712/article/details/107644256
        return parseDict(body, objectMapper.valueToTree(body));
    }

    /**
     * 递归遍历 JSON 树，判断类型，解析字典
     *
     * @see AnnotationUtils
     * @see AnnotatedElementUtils
     * @see BeanUtils#isSimpleValueType
     */
    JsonNode parseDict(Object result, JsonNode jsonNode) {
        if (jsonNode instanceof ArrayNode) {
            ArrayNode arrayNode = (ArrayNode) jsonNode;
            List<Object> list = result.getClass().isArray() ? asList((Object[]) result) : castList(result, Object.class);
            for (int i = 0; i < arrayNode.size(); i++) {
                arrayNode.set(i, parseDict(list.get(i), arrayNode.get(i)));
            }
        } else if (jsonNode instanceof ObjectNode) {
            ObjectNode objectNode = (ObjectNode) jsonNode;
            // 反射获取所有属性
            Map<String, Field> fieldMap = getAllField(result).stream().collect(Collectors.toMap(Field::getName, f -> f));

            Iterator<String> fieldNames = objectNode.fieldNames();
            ObjectNode dictObjectNode = objectMapper.createObjectNode();
            while (fieldNames.hasNext()) {
                String fieldName = fieldNames.next();
                Field field = fieldMap.get(fieldName);
                // is 开头的属性序列化后 name 中会去掉 is，反射获取需要再加上，且属性首字符要大写
                field = nonNull(field) ? field : fieldMap.get((IS + StringUtils.capitalize(fieldName)).intern());
                // jackson 序列化按照 get 方法来，存在没有对象属性，只有 get 方法的情况，反射获取就会为 null
                if (isNull(field)) {
                    continue;
                }
                JsonNode node = objectNode.get(fieldName);

                Dict dict = AnnotatedElementUtils.findMergedAnnotation(field, Dict.class);
                if (nonNull(dict)) {
                    String dictTranslate = getDictTranslate(dict, node.asText());
                    if (nonNull(dictTranslate)) {
                        // 翻译后的属性暂时添加到 dictObjectNode，属性名加 _text 后缀
                        // 直接添加到 objectNode，会造成迭代器出现 ConcurrentModificationException
                        dictObjectNode.put((fieldName + DICT_LABEL_SUFFIX).intern(), dictTranslate);
                    }
                } else {
                    ReflectionUtils.makeAccessible(field);
                    Object fieldValue = ReflectionUtils.getField(field, result);
                    // 递归，替换不会出现 ConcurrentModificationException
                    objectNode.replace(fieldName, parseDict(fieldValue, node));
                }
            }
            // 添加翻译后的属性
            objectNode.setAll(dictObjectNode);
        }
        return jsonNode;
    }

    String getDictTranslate(Dict dict, String code) {
        String type = dict.type();
        Class<? extends DictEnum> dictEnum = dict.dictEnum();

        String name = null;
        if (StringUtils.hasLength(type) && nonNull(dictionaryTranslate)) { // 优先数据库字典表翻译
            name = dictionaryTranslate.getNameByTypeAndCode(type, code);
        }
        if (isNull(name) && !dictEnum.equals(DictEnum.class)) { // 枚举翻译
            name = Arrays.stream(dictEnum.getEnumConstants()).filter(
                x -> x.getValue().equalsIgnoreCase(code)
            ).map(DictEnum::getDesc).findFirst().orElse(null);
        }
        return name;
    }
}